package com.ktjy.auth.util;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.security.Keys;

import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * JWT工具类
 *
 * @author LILIBO
 * @since 2021-06-24
 */
public class JWTUtil {

    /**
     * 时间格式化
     */
    public static final SimpleDateFormat SDF = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    /**
     * 有效期
     */
    private static final Long JWT_TTL = 24 * 60 * 60L; // 一天

    /**
     * 加密算法
     */
    private static final String ALGORITHM = "HmacSHA256";
    /**
     * 秘钥明文
     */
    private static final String JWT_KEY = "BOBOBOBOBOBOBOBOBOBOBOBOBOBOBOBOBOBOBOBOBOBOBOBO";

    /**
     * 发行人
     */
    private static final String JWT_ISSUER = "BoBo";

    /**
     * 生成随机字符串UUID
     *
     * @return
     */
    public static String generateUUID() {
        return UUID.randomUUID().toString().replaceAll("-", "");
    }

    /**
     * 生成加密后的秘钥SecretKey
     * <p>
     * 注意：打死也不要告诉别人
     *
     * @return
     */
    public static SecretKey generalKey() {
        byte[] encodedKey = Base64.getDecoder().decode(JWTUtil.JWT_KEY);
        return new SecretKeySpec(encodedKey, ALGORITHM);
    }

    /**
     * 获取JWT构建器（使用HS256对称加密算法签名）
     *
     * @param uuid 唯一序号
     * @param subject 主题（可以存放JSON数据）
     * @param ttlMillis 过期时间
     * @return
     */
    private static JwtBuilder getJwtBuilder(String uuid, String subject, Long ttlMillis) {
        long nowMillis = System.currentTimeMillis();
        return Jwts.builder().id(uuid).subject(subject).issuer(JWTUtil.JWT_ISSUER).issuedAt(new Date(nowMillis)).expiration(new Date(nowMillis + ttlMillis * 1000L)).signWith(Keys.hmacShaKeyFor(generalKey().getEncoded()));
    }

    /**
     * 获取JWT构建器，允许自定义参数集（使用HS256对称加密算法签名）
     *
     * @param uuid 唯一序号
     * @param subject 主题（可以存放JSON数据）
     * @param params 自定义参数集合
     * @param ttlMillis 过期时间
     * @return
     */
    private static JwtBuilder getJwtBuilder(String uuid, String subject, Map<String, Object> params, Long ttlMillis) {
        long nowMillis = System.currentTimeMillis();
        return Jwts.builder().claims(params).id(uuid).subject(subject).issuer(JWTUtil.JWT_ISSUER).issuedAt(new Date(nowMillis)).expiration(new Date(nowMillis + ttlMillis * 1000L)).signWith(Keys.hmacShaKeyFor(generalKey().getEncoded()));
    }

    /**
     * 生成JWT
     *
     * @param subject 主题（可以存放JSON数据）
     * @return
     */
    public static String createJWT(String subject) {
        JwtBuilder builder = getJwtBuilder(generateUUID(), subject, JWTUtil.JWT_TTL);
        return builder.compact();
    }

    /**
     * 生成JWT
     *
     * @param subject 主题（可以存放JSON数据）
     * @param ttlMillis Token超时时间（毫秒）
     * @return
     */
    public static String createJWT(String subject, Long ttlMillis) {
        JwtBuilder builder = getJwtBuilder(generateUUID(), subject, ttlMillis);
        return builder.compact();
    }

    /**
     * 生成JWT
     *
     * @param params 参数（可以存放简单的用户信息）
     * @return
     */
    public static String createJWT(Map<String, Object> params) {
        JwtBuilder builder = getJwtBuilder(generateUUID(), null, params, JWTUtil.JWT_TTL);
        return builder.compact();
    }

    /**
     * 生成JWT
     *
     * @param id 唯一标记
     * @param subject 主题（可以存放JSON数据）
     * @param ttlMillis 超时时间
     * @return
     */
    public static String createJWT(String id, String subject, Long ttlMillis) {
        JwtBuilder builder = getJwtBuilder(id, subject, ttlMillis);
        return builder.compact();
    }

    /**
     * 生成JWT
     *
     * @param id 唯一标记
     * @param subject 主题（可以是JSON格式）
     * @param params 自定义参数集合
     * @param ttlMillis token超时时间
     * @return
     */
    public static String createJWT(String id, String subject, Map<String, Object> params, Long ttlMillis) {
        JwtBuilder builder = getJwtBuilder(id, subject, params, ttlMillis);
        return builder.compact();
    }

    /**
     * 生成JWT
     *
     * @param subject 主题（可以是JSON格式）
     * @param params 自定义参数集合
     * @param ttlMillis token超时时间
     * @return
     */
    public static String createJWT(String subject, Map<String, Object> params, Long ttlMillis) {
        JwtBuilder builder = getJwtBuilder(generateUUID(), subject, params, ttlMillis);
        return builder.compact();
    }

    /**
     * 生成JWT
     *
     * @param params 参数（可以存放简单的用户信息）
     * @param ttlMillis Token超时时间
     * @return
     */
    public static String createJWT(Map<String, Object> params, Long ttlMillis) {
        JwtBuilder builder = getJwtBuilder(generateUUID(), null, params, ttlMillis);
        return builder.compact();
    }

    /**
     * 生成JWT
     *
     * @param subject 主题（可以存放JSON数据）
     * @param params 自定义参数集合
     * @return
     */
    public static String createJWT(String subject, Map<String, Object> params) {
        JwtBuilder builder = getJwtBuilder(generateUUID(), subject, params, JWTUtil.JWT_TTL);
        return builder.compact();
    }

    /**
     * 解析JWT
     *
     * @param jwt JWT字符串
     * @return
     */
    public static Claims parseJWT(String jwt) {
        return Jwts.parser().verifyWith(generalKey()).build().parseSignedClaims(jwt).getPayload();
    }

    public static void main(String[] args) throws Exception {
        // 用户使用账号、密码完成了登录功能，从数据库中查询出了用户对象
        String uid = "1"; // // 生成JWT（将用户ID等信息载入主题信息）
        Map<String, Object> params = new HashMap<>();
        params.put("account", "LiLiBo"); // 设置自定义参数（不要把敏感信息放到JWT中）
        // String token = createJWT(uid, params);
        String token = createJWT(uid, params, 10L);

        // 获取JWT的token
        System.out.println("生成令牌： " + token);
        System.out.println("===========================================");

        String[] split = token.split("[.]");
        System.out.println("头部信息： " + new String(Base64.getUrlDecoder().decode(split[0])));
        System.out.println("有效载荷： " + new String(Base64.getUrlDecoder().decode(split[1])));
        // 签名是无法解密出来的，直接输出
        System.out.println("数据签名： " + split[2]);
        System.out.println("===========================================");

        // 过期时间之后，解析失败
        // Thread.sleep(10 * 1000L);

        // 解析JWT（Token字符串）
        Claims claims = parseJWT(token);
        // Claims claims = parseJWT("eyJhbGciOiJIUzI1NiJ9.eyJhY2NvdW50IjoiTGlMaUJvIiwianRpIjoiMjM3ZTBiOWNhMGIyNDNhODg2YzZiNTE0YTI2ZDNiZmEiLCJzdWIiOiIxIiwiaXNzIjoiQm9CbyIsImlhdCI6MTc0NDI1ODAzOSwiZXhwIjoxNzQ0MjU4MDQ5fQ.XREsTvSFE1zFFk2NuloUxU6qDuDebk-mSoM6YCMK9e8");

        System.out.println("唯一标识： " + claims.getId());
        System.out.println("内容主题： " + claims.getSubject());
        System.out.println("扩展属性： " + claims.get("account"));
        System.out.println("签发时间： " + SDF.format(claims.getIssuedAt()));
        System.out.println("过期时间： " + SDF.format(claims.getExpiration()));
    }

}